#include "SSCTDTools.h"

#include <bondlib/BondCalendar.h>

#include <QObject>
#include <QStringList>

void SSCTDTools::GetPreDay(int& nYear, int& nMonth, int& nDay) {
  if (nDay > 1) {
    nDay -= 1;
    return;
  }

  if (nMonth == 1) {
    nYear -= 1;
    nMonth = 12;
    nDay = SSCTDTools::MonthDays(nYear, nMonth);
  } else {
    nMonth -= 1;
    nDay = SSCTDTools::MonthDays(nYear, nMonth);
  }
}

void SSCTDTools::GetNextDay(int& nYear, int& nMonth, int& nDay) {
  int nMonthDays = SSCTDTools::MonthDays(nYear, nMonth);
  if (nDay < nMonthDays)
    nDay++;
  else {
    if (nMonth == 12) {
      nYear++;
      nMonth = 1;
      nDay = 1;
    } else {
      nMonth++;
      nDay = 1;
    }
  }
}

int SSCTDTools::MonthDays(unsigned short iYear, unsigned short iMonth) {
  switch (iMonth) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
      return 31;
      break;
    case 4:
    case 6:
    case 9:
    case 11:
      return 30;
      break;
    case 2: {
      if (SSCTDTools::IsLeapYear(iYear))
        return 29;
      else
        return 28;
    }

    break;
  }

  return 0;
}

bool SSCTDTools::IsLeapYear(int iYear) {
  return !(iYear % 4) && (iYear % 100) || !(iYear % 400);
}

int SSCTDTools::GetWeekDay(int iYear, int iMonth, int iDay) {
  // 数组元素monthday[i]表示第i个月以前的总天数除以7的余数
  unsigned short monthday[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
  unsigned short iDays =
      (iYear - 1) % 7 + (iYear - 1) / 4 - (iYear - 1) / 100 + (iYear - 1) / 400;
  iDays += (monthday[iMonth - 1] + iDay);
  // 如果iYear是闰年
  if (SSCTDTools::IsLeapYear(iYear) && iMonth > 2) iDays++;
  // 返回:0,1,2,3,4,5,6表日、一、二、三、四、五、六
  return iDays % 7;
}

void SSCTDTools::GetNextCFFEXTradingDay(time_t& tm, bool bWeekend) {
  GetNextTradingDay(tm, "CNY", "SSE", bWeekend);
}

void SSCTDTools::GetNextTradingDay(time_t& tm, bool bWeekend /*=false*/) {
  GetNextTradingDay(tm, "CNY", "CIB", bWeekend);
}

void SSCTDTools::GetNextTradingDay_SSE(time_t& tm, bool bWeekend /* = false*/) {
  GetNextTradingDay(tm, "CNY", "SSE", bWeekend);
}

void SSCTDTools::GetNextTradingDay(time_t& tm, const char* cCountry,
                                   const char* cMarket, bool bWeekend) {
  QDateTime ctm = QDateTime::fromTime_t(tm);
  int nYear = ctm.date().year();
  int nMonth = ctm.date().month();
  int nDay = ctm.date().day();

  while (1) {
    SSCTDTools::GetNextDay(nYear, nMonth, nDay);
    int nDate = nYear * 10000 + nMonth * 100 + nDay;
    if (!CBondCalendar::instance().GetHolidayByDate(cCountry, cMarket, nDate)) {
      if (bWeekend) {
        int nWeekday = GetWeekDay(nYear, nMonth, nDay);
        if (nWeekday == 0 || nWeekday == 6) continue;
      }
      tm = CBondCalendar::instance().IntToTime(nDate);
      break;
    }
  }
}

void SSCTDTools::GetPreTradingDay(time_t& tm, bool bWeekend) {
  GetPreTradingDay(tm, "CNY", "CIB", bWeekend);
}

void SSCTDTools::GetPreCFFEXTradingDay(time_t& tm, bool bWeekend) {
  GetPreTradingDay(tm, "CNY", "SSE", bWeekend);
}

void SSCTDTools::GetPreTradingDay_SSE(time_t& tm, bool bWeekend) {
  GetPreTradingDay(tm, "CNY", "SSE", bWeekend);
}

bool SSCTDTools::IsCFFEXHoliday(int nDate) {
  if (!CBondCalendar::instance().GetHolidayByDate("CNY", "SSE", nDate)) {
    if (nDate < 10000) return false;
    int nYear = nDate / 10000;
    int nMonth = (nDate % 10000) / 100;
    int nDay = nDate % 100;
    int nWeekday = GetWeekDay(nYear, nMonth, nDay);
    if (nWeekday == 0 || nWeekday == 6) {
      return true;
    }
    return false;
  }
  return true;
}

void SSCTDTools::GetPreTradingDay(time_t& tm, const char* cCountry,
                                  const char* cMarket, bool bWeekend) {
  if (strlen(cMarket) <= 0 || strlen(cCountry) <= 0) return;
  QDateTime ctm = QDateTime::fromTime_t(tm);
  int nYear = ctm.date().year();
  int nMonth = ctm.date().month();
  int nDay = ctm.date().day();

  while (1) {
    SSCTDTools::GetPreDay(nYear, nMonth, nDay);
    int nDate = nYear * 10000 + nMonth * 100 + nDay;
    if (!CBondCalendar::instance().GetHolidayByDate(cCountry, cMarket, nDate)) {
      if (bWeekend) {
        int nWeekday = GetWeekDay(nYear, nMonth, nDay);
        if (nWeekday == 0 || nWeekday == 6) continue;
      }
      tm = CBondCalendar::instance().IntToTime(nDate);
      break;
    }
  }
}
